"""
从我方sqlserver [huada]数据库中获取数据，并写入到我方系统中
"""

from config import Config
from plugin import Mylogger, MSsqlClient, SchedulerManager, Trigger
from module.huada.entity import *
import traceback
# from copy import deepcopy

do_test = Config.module_config.huada.do_test
delaytime = Config.module_config.huada.write_data_delay

# 同步时，一些表的优先级，优先级越低，越先同步
# 优先同步 部门，区域，虚拟卡，城市
# sync_priv = {}
sync_priv = {
    # O2Entity: 1,
    # O3Entity: 1,
    # O4Entity: 1,
    # O5Entity: 1,
    # O6Entity: 1,
    # O7Entity: 1,
    # AreaEntity: 1,
    PersonEntity: 2,
    # DoorsEntity: 2,
    VisitorEntity: 2,
    UserCardEntity: 3,
    # VirtualCardEntity: 3,
    AuthEntity: 4
}

retry_times = 3


def check_priv(cls):
    """
    检查同步优先级
    原理：
        每次检查优先级是否最低，如果不是，则不同步
        优先级最低的同步完成后(标志为取值为0)，将该任务从优先级字典中删除，保证其他任务的优先级最低
        所以，如果字典中不存在的，或者，当前任务的优先级最低，则同步
    """
    if len(sync_priv) > 0:
        min_priv = min(sync_priv.values())
        if sync_priv.__contains__(cls) and sync_priv[cls] > min_priv:
            Mylogger.info(
                f"优先级列表：{sync_priv}，当前任务优先级为{sync_priv[cls]}，最低优先级为{min_priv}，不同步")
            return False

    return True


def sync(cls: BaseEntity, size=1000):
    """
    从我方sqlserver [huada]数据库中获取数据，并写入到我方系统中
    :param cls: BaseEntity的子类
    :return:
    """
    if not check_priv(cls):
        return
    try:
        client = MSsqlClient(charset='cp936', **
                             Config.module_config.huada.record_link)
        # 从中间库获取获取数据
        results = client.select_to_dict("select top({}) * from {} where isdeal < 1 and isdeal > -{} order by {}".format(
            size, cls.table_name, retry_times, cls.order_by))
        if sync_priv.__contains__(cls) and results is not None and len(results) == 0:
            # 从优先级字典中删除
            del sync_priv[cls]
            Mylogger.info(f"{cls.table_name} 初始数据同步完成，从优先级字典中删除")
        Mylogger.info(f"get {len(results)} records from db {cls.table_name}")
        for result in results:
            cls.append_sync(cls(**result))  # 数据放入对应类的缓存中

        # 同步到组态数据库
        objs = cls.sync_list.copy()  # 复制一份待处理数据，防止sync_list更新，数据错乱
        if cls.do_sync(MSsqlClient(**Config.module_config.huada.client_link), objs):
            # Mylogger.info(f"sync {len(results)} records from db {cls.table_name} success")
            cls.sync_list = list(set(cls.sync_list).difference(set(objs)))  # 防止取数据的频率比处理数据快

            # 回写状态
            cls.write_back()
        objs.clear()
    except Exception as e:
        Mylogger.error(f"sync {cls.table_name} error: {e}")
        Mylogger.error("--------------------------------------------")
        Mylogger.error(f"{traceback.format_exc()}")


def sync_auth(sql, method, datalist: list, write_back_dict: dict):
    if not check_priv(AuthEntity):
        return
    try:
        client = MSsqlClient(charset='cp936', **
                             Config.module_config.huada.record_link)
        results = client.select_to_dict(sql)
        if sync_priv.__contains__(AuthEntity) and results and len(results) == 0:
            # 从优先级字典中删除
            del sync_priv[AuthEntity]
            Mylogger.info(f"{AuthEntity.table_name} 初始数据同步完成，从优先级字典中删除")
        Mylogger.info(
            f"get {len(results)} records from db {AuthEntity.table_name}")
        for result in results:
            datalist.append(AuthEntity(**result))

        objs = datalist.copy()
        if method(MSsqlClient(**Config.module_config.huada.client_link), objs):
            # datalist = list(set(datalist).difference(set(objs)))
            # datalist 减去 objs
            for obj in objs:
                datalist.remove(obj)

            # 回写状态
            AuthEntity.write_back_from_dict(write_back_dict)
    except Exception as e:
        Mylogger.error(f"sync {AuthEntity.table_name} error: {e}")
        Mylogger.error("--------------------------------------------")
        Mylogger.error(f"{traceback.format_exc()}")


def sync_down_auth(size=100000):
    sql = 'select top({}) * from {} where begintime < DATEADD(MI,10,getdate()) and isdeal < 1 and isdeal > -{} and authstate = 1 order by {}'.format(
        size, AuthEntity.table_name, retry_times, AuthEntity.order_by)
    sync_auth(sql, AuthEntity.down_access,
              AuthEntity.down_access_list, AuthEntity.write_back_dict)


def sync_cancel_auth(size=100000):
    sql = 'select top({}) * from {} where ((endtime < DATEADD(MI,10,getdate()) and isdeal = 1) or authstate = 2) and isdeal not in (2, -99) order by {}'.format(
        size, AuthEntity.table_name, AuthEntity.order_by)
    sync_auth(sql, AuthEntity.cancel_access,
              AuthEntity.cancel_access_list, AuthEntity.write_back_dict_cancel)


@SchedulerManager.Scheduler(Trigger.interval(minutes=3), delay=delaytime * 60)
def sync_department():
    sync(O2Entity, 3000)
    sync(O3Entity, 3000)
    sync(O4Entity, 3000)
    sync(O5Entity, 3000)
    sync(O6Entity, 3000)
    sync(O7Entity, 3000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=4), delay=(delaytime + 1) * 60)
def sync_person():
    sync(PersonEntity, 50000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=4), delay=(delaytime + 2) * 60)
def sync_visitor():
    sync(VisitorEntity, 50000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=1))
def sync_usercard():
    sync(UserCardEntity, 50000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=1))
def sync_visitorcard():
    sync(VisitorCardEntity, 50000)


@SchedulerManager.Scheduler(Trigger.interval(hours=24), delay=delaytime * 60)
def sync_virtualcard():
    sync(VirtualCardEntity, 200000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=3), delay=delaytime * 60)
def sync_area():
    sync(AreaEntity, 10000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=4))
def sync_doors():
    sync(DoorsEntity, 10000)


@SchedulerManager.Scheduler(Trigger.interval(minutes=3), delay=1)
def sync_doorsforbid1():
    sync_down_auth()


@SchedulerManager.Scheduler(Trigger.interval(minutes=3))
def sync_doorsforbid2():
    sync_cancel_auth()


# # 第一批同步
sync_department()
sync_area()
sync_virtualcard()
# # 第二批同步
sync_person()
sync_visitor()
sync_doors()
# # 第三批同步
sync_usercard()
sync_visitorcard()
# 第四批同步
sync_doorsforbid1()
sync_doorsforbid2()
